/*
 * XmlUtils.cpp
 *
 * @author Bematech S.A.
 */

#include "bilbao/util/XmlUtils.h"

namespace bilbao
{
	namespace util
	{
		//--------------------------------------------------------------------------------
		// Métodos Públicos
		//--------------------------------------------------------------------------------
		XmlUtils::XmlUtils( void )
		{
			try
			{
				XMLPlatformUtils::Initialize(); // Initialize Xerces infrastructure
			}
			catch ( XMLException& e )
			{
				char* message = XMLString::transcode( e.getMessage() );
				//TODO: LOG(....)
				std::cerr << "XML toolkit initialization error: " << message << std::endl;
				XMLString::release( &message );
			}

			//Configurando...
			_configParser = new XercesDOMParser;
			_configParser->setValidationScheme( XercesDOMParser::Val_Never );
			//_configParser->setValidationScheme( XercesDOMParser::Val_Always );
			_configParser->setDoNamespaces( false );
			_configParser->setDoSchema( false );
			_configParser->setLoadExternalDTD( false );
			//_configParser->setLoadExternalDTD( true );

			//_configParser->loadGrammar("nome_do_dtd.dtd", Grammar::DTDGrammarType, true);
			//_configParser->loadGrammar("nome_do_xsd.xsd", Grammar::SchemaGrammarType, true);

			//DefaultHandler defaultHandler;
			//_configParser->setContentHandler(&defaultHandler);
			//_configParser->setErrorHandler(&defaultHandler);

			try
			{
				XMLCh* CORE = XMLString::transcode( "Core" ); // xerces example used "Core", no idea if that is necessary.
				mImplementation = DOMImplementationRegistry::getDOMImplementation( CORE );
				XMLString::release( &CORE );
			}
			catch ( ... )
			{
				//LOG_ERROR	("There was a problem creating a Xerces DOMImplementation.")
				mImplementation = 0;
			}
		}
		//--------------------------------------------------------------------------------
		XmlUtils::~XmlUtils( void )
		{
			if ( _configParser )
			{
				delete _configParser;
			}

			// Terminate Xerces
			try
			{
				XMLPlatformUtils::Terminate(); // Terminate after release of memory
			}
			catch ( xercesc::XMLException& e )
			{
				char* message = xercesc::XMLString::transcode( e.getMessage() );
				//TODO: LOG(...);
				std::cerr << "XML tolkit teardown error: " << message << std::endl;
				XMLString::release( &message );
			}
		}
		//--------------------------------------------------------------------------------
		XML XmlUtils::data2xml( XML xml )
		{
			//TODO: implementar
			return NULL;
		}
		//--------------------------------------------------------------------------------
		String XmlUtils::getDigitalCertificate( XML xml )
		{
			//TODO: implementar

			return NULL;
		}
		//--------------------------------------------------------------------------------
		long XmlUtils::getPeriodicityTime( XML useParameters )
		{
			//TODO: implementar

			return 0L;
		}
		//--------------------------------------------------------------------------------
		void XmlUtils::string2File( const String& outputFile, const String& str )
		{
			std::ofstream outfile( outputFile.c_str(), std::ofstream::out );

			// write to outfile
			outfile.write( str.c_str(), str.length() );
			outfile.close();
		}
		//--------------------------------------------------------------------------------
		int XmlUtils::string2Mem( const String& xml, const String& memBufId, DOMDocument** doc ) const
		{
			int errorCode;

			//TODO: fazer gerenciamento dos IDs
			MemBufInputSource* memBuf = new MemBufInputSource( (const XMLByte*) xml.c_str(), xml.length(), memBufId.c_str(), //IDs
					false );

			try
			{
				_configParser->parse( *memBuf );

				if ( doc )
				{
					*doc = _configParser->getDocument();
				}
			}
			catch ( const OutOfMemoryException& )
			{
				//XERCES_STD_QUALIFIER cerr << "OutOfMemoryException" << XERCES_STD_QUALIFIER endl;
				errorCode = 5;
			}
			catch ( const XMLException& e )
			{
				/*XERCES_STD_QUALIFIER cerr << "\nError during parsing memory stream:\n"
				 << "Exce				ption message is:  \n"
				 << StrX(e.getMessage()) << "\n" << XERCES_STD_QUALIFIER endl;*/
				errorCode = 4;
			}

			delete memBuf;
			return errorCode;
		}
		//--------------------------------------------------------------------------------
		int XmlUtils::string2DOMDoc( const String& xml, DOMDocument** doc )
		{
			//String 2 Mem
			return -1;
		}
		//--------------------------------------------------------------------------------
		int XmlUtils::file2String( const String& inputfile, String& ret )
		{
			int errorCode = 0;

			//Ler string do arquivo
			std::stringstream xmlStr;
			String line;
			std::ifstream xmlFile( inputfile.c_str() ); // ifstream = padrão ios:in

			if ( xmlFile.is_open() )
			{
				//enquanto end of file for false continua
				while ( !xmlFile.eof() )
				{
					getline( xmlFile, line ); // como foi aberto em modo texto(padrão)
					//e não binário(ios::bin) pega cada linha
					xmlStr << line << std::endl;
				}

				xmlFile.close();
			}

			else
				errorCode = -1; /*LOG(...)*/

			ret = xmlStr.str();

			return errorCode;
		}
		//--------------------------------------------------------------------------------
		int XmlUtils::file2Mem( const String& inputfile, const char* memBufId, DOMDocument** doc ) const
		{
			int errorCode = 0;
			String xmlStr = "";
			file2String( inputfile, xmlStr );

			//TODO: fazer gerenciamento dos IDs
			MemBufInputSource* memBuf = new MemBufInputSource( (const XMLByte*) xmlStr.c_str(), xmlStr.length(), memBufId, //IDs
					false );

			try
			{
				_configParser->parse( *memBuf );

				if ( doc )
				{
					*doc = _configParser->getDocument();

					try
					{
						_configParser->parse( *memBuf );

						if ( doc )
						{
							*doc = _configParser->getDocument();
						}
					}
					catch ( const OutOfMemoryException& e )
					{
						//XERCES_STD_QUALIFIER cerr << "OutOfMemoryException" << XERCES_STD_QUALIFIER endl;
						LOG_E( X(e.getMessage()) );
						errorCode = 5;
					}
					catch ( const XMLException& e )
					{
						/*XERCES_STD_QUALIFIER cerr << "\nError during parsing memory stream:\n"
						 << "Exce				ption message is:  \n"
						 << StrX(e.getMessage()) << "\n" << XERCES_STD_QUALIFIER endl;*/
						LOG_E( X(e.getMessage()) );
						errorCode = 4;
					}

					delete memBuf;
				}
			}
			catch ( const OutOfMemoryException& e )
			{
				//XERCES_STD_QUALIFIER cerr << "OutOfMemoryException" << XERCES_STD_QUALIFIER endl;
				LOG_E( X(e.getMessage()) );
				errorCode = 5;
			}
			catch ( const XMLException& e )
			{
				/*XERCES_STD_QUALIFIER cerr << "\nError during parsing memory stream:\n"
				 << "Exce				ption message is:  \n"
				 << StrX(e.getMessage()) << "\n" << XERCES_STD_QUALIFIER endl;*/
				LOG_E( X(e.getMessage()) );
				errorCode = 4;
			}

			//delete memBuf;
			return errorCode;
		}
		//--------------------------------------------------------------------------------
		int XmlUtils::DOMDoc2File( const DOMDocument* doc, const String& outputfile ) const
		{
			int errorCode = 0;

			if ( !doc )
				return -1; //TODO: Log
			if ( !mImplementation )
				return -1; //TODO: Log

			XMLCh* OUTPUT;

			try
			{
				DOMLSOutput* xmlstream;
				DOMLSSerializer* writer;

				OUTPUT = XMLString::transcode( outputfile.c_str() );
				xmlstream = mImplementation->createLSOutput();
				xmlstream->setSystemId( OUTPUT );
				writer = mImplementation->createLSSerializer();

				DOMConfiguration* dc = writer->getDomConfig();
				dc->setParameter( XMLUni::fgDOMWRTFormatPrettyPrint, true );

				try
				{
					if ( !writer->write( doc, xmlstream ) )
					{
						errorCode = 2;
					}
				}
				catch ( const OutOfMemoryException& e )
				{
					errorCode = 3;
				}
				catch ( const DOMException& )
				{
					//XERCES_STD_QUALIFIER cerr << "DOMException code is:  " << e.code << XERCES_STD_QUALIFIER endl;
					//LOG_ERROR("When writing file, " +outputfile+ ", a DOMException occurred");
					errorCode = 4;
				}

				writer->release();
				xmlstream->release();
			}
			catch ( ... )
			{
				if ( OUTPUT )
					XMLString::release( &OUTPUT );
				errorCode = 1;
			}

			return errorCode;
		}
		//--------------------------------------------------------------------------------
		int XmlUtils::DOMDoc2String( const DOMDocument* doc, String& outputStr ) const
		{
			//TODO: implementar
			return 0;
		}
		//--------------------------------------------------------------------------------
		DOMElement* XmlUtils::addNewTag( DOMDocument** doc, DOMElement** parent, const String& tagName )
		{
			if ( !doc || !parent )
				return NULL; //TODO: LOG

			DOMElement* tag = NULL;

			try
			{
				tag = ( *doc )->createElement( X(tagName.c_str()) );
				( *parent )->appendChild( tag );
			}
			catch ( xercesc::XMLException& e )
			{
				//TODO: LOG
				std::cout << "[Exception]\n" << X(e.getMessage()) << std::endl;
				tag = NULL;
			}

			return tag;
		}
		//--------------------------------------------------------------------------------
		void XmlUtils::addNewTagRoot( DOMDocument** doc, const String& tagName, const String& value )
		{
			if ( !doc )
				return; //TODO: LOG

			try
			{
				//Adicionando um elemento...
				DOMElement* elementRoot = ( *doc )->getDocumentElement();

				DOMElement* tag = ( *doc )->createElement( X(tagName.c_str()) );
				tag->setTextContent( X(value.c_str()) );

				elementRoot->appendChild( tag );
			}
			catch ( xercesc::XMLException& e )
			{
				//TODO: LOG
			}
		}
		//--------------------------------------------------------------------------------
		int XmlUtils::getElementByTagName( /*in*/const String& tagName, /*in*/const DOMDocument* doc, /*out*/DOMElement** element )
		{
			XMLSize_t nodeCount = -1;

			try
			{
				DOMElement* elementRoot = doc->getDocumentElement();
				if ( !elementRoot )
				{
					LOG_E("empty XML document");
					return -1; //throw(std::runtime_error("empty XML document"));
				}

				DOMNodeList* list = doc->getElementsByTagName( X(tagName.c_str()) );

				nodeCount = list->getLength();

				if ( nodeCount == 1 )
				{
					DOMNode* currentNode = list->item( 0 );
					DOMElement* currentElement = dynamic_cast< xercesc::DOMElement* > ( currentNode );
					( *element ) = currentElement;
				}
			}
			catch ( xercesc::XMLException& e )
			{
				char* message = xercesc::XMLString::transcode( e.getMessage() );
				//StringStream errBuf;
				//errBuf << "Error parsing file: " << message << flush;
				LOG_E( "Error parsing file: ");
				LOG_E( message );
				XMLString::release( &message );
			}

			return nodeCount;
		}
		//--------------------------------------------------------------------------------
		void XmlUtils::addAdcfe( XmlUtils& XMLUTILS, DOMDocument* DOC, DOMElement* PARENT_TAG_NODE,
				const String& TAG_NODE, const String& TAG_CONTENT, DOMElement* NODE)
		{
			NODE = XMLUTILS.addNewTag( &DOC, &PARENT_TAG_NODE, TAG_NODE );
			NODE->setTextContent( X( TAG_CONTENT.c_str()));
		}
		//--------------------------------------------------------------------------------
		void XmlUtils::addAdcfeNode( XmlUtils& XMLUTILS, DOMDocument* DOC, DOMElement* PARENT_TAG_NODE, const String& TAG_NODE, DOMElement* NODE)
		{
			NODE = XMLUTILS.addNewTag( &DOC, &PARENT_TAG_NODE, TAG_NODE );
		}
		//--------------------------------------------------------------------------------
		void XmlUtils::conteudo( XmlUtils& XMLUTILS, DOMDocument* DOC,const String& PATH_TAG_NODE, DOMElement*/*out*/ELEMENT, String& /*in*/ CONTENT )
		{
			XMLUTILS.getElementByTagPath( PATH_TAG_NODE, DOC, &ELEMENT );
			CONTENT = ( ELEMENT ) ? X( ELEMENT->getTextContent() ) : ""; /*EMPTY_STRING_XML*/
		}
		//--------------------------------------------------------------------------------
		int XmlUtils::getElementByTagPath( /*in*/const String& pathName, /*in*/const DOMDocument* doc, /*out*/DOMElement** ret )
		{
			Tokenizer strToken( pathName );
			Vector<String> tokens;

			tokens = strToken.split();
			int szToken = tokens.size();

			if ( !szToken ) return 0;

			try
			{
				DOMElement* root = doc->getDocumentElement();
				DOMNodeList* listNode = root->getElementsByTagName(X( tokens[szToken-1].c_str()));
				DOMNode* node;
				DOMNode* nodeAux;
				DOMNode* parentNode;

				int targetParent = szToken-2;

				for ( int i=0; i < (int)listNode->getLength(); ++i )
				{
					node = listNode->item(i);
					nodeAux = node;
					targetParent = szToken-2;

					while( targetParent >= 0 )
					{
						parentNode = nodeAux->getParentNode();

						String temp = X(parentNode->getNodeName());
						if ( temp == tokens[targetParent] )
						{
							targetParent--;
							nodeAux = nodeAux->getParentNode();
						}
						else
						{
							break;
						}
					}

					if ( targetParent < 0 )
					{
						*ret = dynamic_cast<DOMElement*>(node);
						return 1/*SUCESSO*/;
					}
				}

			}
			catch ( xercesc::XMLException& e )
			{
				LOG_E( X(e.getMessage()) );
			}

			return 0/*ERRO*/;
		}
		//--------------------------------------------------------------------------------
	} // namespace util
} // namespace bilbao

